#pragma once 
#include"sock.hpp"
#include<sys/epoll.h>

namespace ns_epoll
{
    using namespace ns_sock;
    const int default_port=8081;

    class Epoll
    {
    private:
        uint16_t _port;
        int _listen_sock;
        int ep_fd;//epoll底层文件句柄
    public:
        Epoll(uint16_t port=default_port):_port(port),_listen_sock(-1),ep_fd(-1){}
        ~Epoll(){
            if(_listen_sock>=0) close(_listen_sock);
            if(ep_fd>=0) close(ep_fd);
        }
        void InitEpollServer()
        {
            _listen_sock=Sock::Socket();
            Sock::Bind(_listen_sock,_port);
            Sock::Listen(_listen_sock);
            //epoll模型创建
            ep_fd=epoll_create(128);
            if(ep_fd<0){
                std::cerr<<"epoll mode creat error..."<<std::endl;
                exit(1);
            }
            //监听文件存入epoll中
            struct epoll_event ev;
            ev.events=EPOLLIN;//关注读事件
            ev.data.fd=_listen_sock;
            epoll_ctl(ep_fd,EPOLL_CTL_ADD,_listen_sock,&ev);
        }
        void HandlerEvent(struct epoll_event evs[],int num)//就绪事件及对应的个数
        {
            //epoll获取到的就是就绪事件的队列
            for(int i=0;i<num;i++)
            {
                if(evs[i].events&EPOLLIN){//读事件就绪
                    if(evs[i].data.fd==_listen_sock){//连接到来
                        struct sockaddr_in peer;
                        socklen_t len=sizeof(peer);
                        bzero(&peer,len);
                        int sock=accept(_listen_sock,(struct sockaddr*)&peer,&len);
                        if(sock>0){
                            //添加文件描述符
                            struct epoll_event ev;
                            ev.data.fd=sock;
                            ev.events=EPOLLIN;
                            epoll_ctl(ep_fd,EPOLL_CTL_ADD,sock,&ev);
                            std::cout<<"新连接到来:"<<sock<<std::endl;
                        }else{
                            std::cerr<<"accept err"<<std::endl;
                            //异常处理...
                        }
                    }else{//数据到来-进行数据读取
                        char buffer[1024]={0};
                        ssize_t s=recv(evs[i].data.fd,buffer,sizeof(buffer),0);
                        if(s>0){
                            buffer[s-1]=0;
                            std::cout<<"buffer:"<<buffer<<std::endl;
                            //接下来关注其写事件是否就绪
                            struct epoll_event ev;
                            ev.data.fd=evs[i].data.fd;
                            ev.events=EPOLLIN|EPOLLOUT;
                            epoll_ctl(ep_fd,EPOLL_CTL_MOD,evs[i].data.fd,&ev);
                        }else if(s==0){//对端连接关闭
                            std::cerr<<"peer sock:"<<evs[i].data.fd<<" close..."<<std::endl;
                            close(evs[i].data.fd);//关闭连接
                            epoll_ctl(ep_fd,EPOLL_CTL_DEL,evs[i].data.fd,nullptr);
                        }else{
                            std::cerr<<"recv err..."<<std::endl;
                        }
                    }
                }
                if(evs[i].events&EPOLLOUT){//写事件就绪
                    std::string message="okkk...";
                    send(evs[i].data.fd,message.c_str(),message.size(),0);
                    //读事件是需要常关注的，写事件是按需关注的
                    struct epoll_event ev;
                    ev.data.fd=evs[i].data.fd;
                    ev.events=EPOLLIN;
                    epoll_ctl(ep_fd,EPOLL_CTL_MOD,evs[i].data.fd,&ev);
                }
                //...
            }
        }
        void Loop()
        {
            #define NUM 10
            struct epoll_event evs[NUM];
            while(true){
                //poll的输入输出参数是分离的，所以这里不用每次都进行初始化文件数组
                //多路转接
                int time=-1;//阻塞式
                int n=epoll_wait(ep_fd,evs,NUM,time);//获取就绪队列的数据
                if (n>0)//有事件就绪
                    HandlerEvent(evs,n);
                else if(n==0)
                    std::cout<<"timeout..."<<std::endl;
                else
                    std::cerr<<"poll err"<<errno<<std::endl;          
            }
        }
    } ;
}